package com.atguigu.tingshu.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.account.client.UserAccountFeignClient;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.TrackInfoFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.constant.TingShuMqConstants;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.service.RabbitmqService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.album.AlbumInfo;
import com.atguigu.tingshu.model.album.TrackInfo;
import com.atguigu.tingshu.model.order.OrderDerate;
import com.atguigu.tingshu.model.order.OrderDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.order.helper.SignHelper;
import com.atguigu.tingshu.order.mapper.OrderDerateMapper;
import com.atguigu.tingshu.order.mapper.OrderDetailMapper;
import com.atguigu.tingshu.order.mapper.OrderInfoMapper;
import com.atguigu.tingshu.order.service.OrderInfoService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.user.client.VipServiceConfigFeignClient;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.atguigu.tingshu.vo.base.user.UserInfo;
import com.atguigu.tingshu.vo.base.user.UserPaidTrack;
import com.atguigu.tingshu.vo.base.user.VipServiceConfig;
import com.atguigu.tingshu.vo.order.OrderDerateVo;
import com.atguigu.tingshu.vo.order.OrderDetailVo;
import com.atguigu.tingshu.vo.order.OrderInfoVo;
import com.atguigu.tingshu.vo.order.TradeVo;
import com.atguigu.tingshu.vo.user.UserPaidRecordVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private VipServiceConfigFeignClient vipServiceConfigFeignClient;
    @Resource
    private UserInfoFeignClient userInfoFeignClient;
    @Resource
    private AlbumInfoFeignClient albumInfoFeignClient;
    @Resource
    private TrackInfoFeignClient trackInfoFeignClient;
    @Resource
    private OrderDetailMapper orderDetailMapper;
    @Resource
    private OrderDerateMapper orderDerateMapper;
    @Resource
    private UserAccountFeignClient userAccountFeignClient;
    @Resource
    private RabbitmqService rabbitmqService;

    @Override
    public OrderInfoVo trade(TradeVo tradeVo) {
        OrderInfoVo orderInfoVo = new OrderInfoVo();
        Long itemId = tradeVo.getItemId();
        String itemType = tradeVo.getItemType();
        Integer trackCount = tradeVo.getTrackCount();
        Long userId = AuthContextHolder.getUserId();
        //原价
        BigDecimal originalAmount = new BigDecimal("0.00");
        //优惠价
        BigDecimal derateAmount = new BigDecimal("0.00");
        //惠后价
        BigDecimal orderAmount = new BigDecimal("0.00");
        //订单明细列表
        List<OrderDetailVo> orderDetailVoList = new ArrayList<>();
        //订单减免明细列表
        List<OrderDerateVo> orderDerateVoList = new ArrayList<>();
        //开通vip
        if (SystemConstant.ORDER_ITEM_TYPE_VIP.equals(itemType)) {
            //根据id查询会员详情
            Result<VipServiceConfig> vipServiceConfigResult = vipServiceConfigFeignClient.getVipConfig(itemId);
            Assert.notNull(vipServiceConfigResult, "生成购买VIP订单时，远程调用 获取VIP详情失败！");
            VipServiceConfig vipServiceConfig = vipServiceConfigResult.getData();
            if (vipServiceConfig == null) return null;
            //金额信息
            originalAmount = vipServiceConfig.getPrice();
            orderAmount = vipServiceConfig.getDiscountPrice();
            derateAmount = originalAmount.subtract(orderAmount);
            //订单明细列表
            orderDetailVoList.add(new OrderDetailVo().setItemId(itemId).setItemName(vipServiceConfig.getName()).setItemUrl(vipServiceConfig.getImageUrl()).setItemPrice(orderAmount));
            //订单减免明细列表
            orderDerateVoList.add(new OrderDerateVo().setDerateType(SystemConstant.ORDER_DERATE_VIP_SERVICE_DISCOUNT).setDerateAmount(derateAmount));
            orderInfoVo.setOrderDerateVoList(orderDerateVoList);
        } else
            //购买专辑
            if (SystemConstant.ORDER_ITEM_TYPE_ALBUM.equals(itemType)) {
                Result<Boolean> result = userInfoFeignClient.isPayAlbum(itemId);
                Assert.notNull(result, "购买专辑时，获取当前用户是否购买过此专辑失败！");
                Boolean flag = result.getData();
                if (flag) throw new GuiguException(ResultCodeEnum.NO_REPEAT_BUY);
                //获取专辑信息
                Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(itemId);
                Assert.notNull(albumInfoResult, "购买专辑时，获取专辑信息失败！");
                AlbumInfo albumInfo = albumInfoResult.getData();
                if (albumInfo == null) return null;
                //获取用户信息
                Result<UserInfo> userInfoResult = userInfoFeignClient.getUserInfo(userId);
                Assert.notNull(userInfoResult, "购买专辑时，获取当前用户信息失败！");
                UserInfo userInfo = userInfoResult.getData();
                if (userInfo == null) return null;
                //判断用户是否是VIP，确定折扣
                BigDecimal discount = new BigDecimal(0);
                if (userInfo.getIsVip() == 1) {
                    discount = albumInfo.getVipDiscount();
                } else {
                    discount = albumInfo.getDiscount();
                }
                originalAmount = albumInfo.getPrice();
                //判断是否打折
                if (discount.intValue() == -1) { //不打折
                    orderAmount = originalAmount;
                } else {//打折
                    orderAmount = originalAmount.multiply(discount.multiply(new BigDecimal("0.1")));
                }
                derateAmount = originalAmount.subtract(orderAmount);
                orderDetailVoList.add(new OrderDetailVo().setItemId(albumInfo.getId()).setItemName(albumInfo.getAlbumTitle()).setItemUrl(albumInfo.getCoverUrl()).setItemPrice(orderAmount));
                orderDerateVoList.add(new OrderDerateVo().setDerateType(SystemConstant.ORDER_DERATE_ALBUM_DISCOUNT).setDerateAmount(derateAmount));
                orderInfoVo.setOrderDerateVoList(orderDerateVoList);
            } else
                //购买声音
                if (SystemConstant.ORDER_ITEM_TYPE_TRACK.equals(itemType)) {
                    if (tradeVo.getTrackCount() <= 0) throw new GuiguException(ResultCodeEnum.ARGUMENT_VALID_ERROR);
                    //查询购买列表
                    Result<List<TrackInfo>> listResult = this.trackInfoFeignClient.findPaidTrack(itemId, trackCount);
                    Assert.notNull(listResult, "购买声音时，获取需要购买的声音列表失败！");
                    List<TrackInfo> trackInfoList = listResult.getData();
                    if (CollectionUtils.isEmpty(trackInfoList)) throw new GuiguException(ResultCodeEnum.DATA_ERROR);
                    //查询声音所属专辑
                    Result<AlbumInfo> albumInfoResult = this.albumInfoFeignClient.getAlbumInfo(trackInfoList.get(0).getAlbumId());
                    Assert.notNull(albumInfoResult, "购买声音时，获取该声音所属的专辑失败！");
                    AlbumInfo albumInfo = albumInfoResult.getData();
                    originalAmount = albumInfo.getPrice().multiply(new BigDecimal(trackInfoList.size()));
                    orderAmount = originalAmount;
                    trackInfoList.forEach(trackInfo -> {
                        orderDetailVoList.add(new OrderDetailVo().setItemId(trackInfo.getId())
                                .setItemName(trackInfo.getTrackTitle()).setItemUrl(trackInfo.getCoverUrl())
                                .setItemPrice(albumInfo.getPrice()));
                    });
                }
        //封装orderInfoVo
        String tradeNo = IdWorker.getIdStr();
        orderInfoVo.setTradeNo(tradeNo);
        //为防止交易号重复使用
        this.redisTemplate.opsForValue().set(RedisConstant.USER_TRADE_PREFIX + tradeNo, tradeNo, RedisConstant.USER_TRADE_TIMEOUT, TimeUnit.SECONDS);
        orderInfoVo.setPayWay(SystemConstant.ORDER_PAY_WAY_WEI_XIN);
        orderInfoVo.setItemType(itemType);
        orderInfoVo.setOriginalAmount(originalAmount);
        orderInfoVo.setDerateAmount(derateAmount);
        orderInfoVo.setOrderAmount(orderAmount);
        orderInfoVo.setOrderDetailVoList(orderDetailVoList);
        //生成时间戳
        orderInfoVo.setTimestamp(System.currentTimeMillis());
        //生成签名
        HashMap hashMap = JSON.parseObject(JSON.toJSONString(orderInfoVo), HashMap.class);
        String sign = SignHelper.getSign(hashMap);
        orderInfoVo.setSign(sign);
        return orderInfoVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitOrder(OrderInfoVo orderInfoVo) {
        ///验签
        HashMap map = JSON.parseObject(JSON.toJSONString(orderInfoVo), HashMap.class);
        //将支付类型改为默认
        map.put("payWay", SystemConstant.ORDER_PAY_WAY_WEI_XIN);
        SignHelper.checkSign(map);
        ///幂等性校验
        String tradeKey = RedisConstant.USER_TRADE_PREFIX + orderInfoVo.getTradeNo();
        //编写lua脚本
        String script = """
                    if redis.call('exists' , KEYS[1]) == 1
                    then
                        return redis.call('del' , KEYS[1])
                    else
                        return 0
                    end           
                """;
        Boolean flag = (Boolean) this.redisTemplate.execute(new DefaultRedisScript(script, Boolean.class), List.of(tradeKey), new ArrayList<>());
        if (Boolean.FALSE.equals(flag)) {
            throw new GuiguException(ResultCodeEnum.ORDER_SUBMIT_REPEAT);
        }
        ///判断支付类型  非账户余额支付则直接保存订单
        if (!SystemConstant.ORDER_PAY_ACCOUNT.equals(orderInfoVo.getPayWay())) {
            this.saveOrder(orderInfoVo, AuthContextHolder.getUserId());
            this.rabbitmqService.sendMqMsg(TingShuMqConstants.ORDER_EXCHANGE, TingShuMqConstants.ORDER_TTL_RK, orderInfoVo.getTradeNo());
            return;
        }
        //账户余额支付
        //1、验余额并锁定余额
        Result<AccountLockResultVo> accountLockResultVoResult = this.userAccountFeignClient.checkAndLockAccount(new AccountLockVo().setUserId(AuthContextHolder.getUserId())
                .setOrderNo(orderInfoVo.getTradeNo()).setAmount(orderInfoVo.getOrderAmount()));
        Assert.notNull(accountLockResultVoResult, "提交订单的时，远程调用验余额锁余额失败！");
        AccountLockResultVo accountLockResultVo = accountLockResultVoResult.getData();
        if (accountLockResultVo == null) throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
        //保存订单信息
        try {
            this.saveOrder(orderInfoVo, accountLockResultVo.getUserId());
            //发消息，进行余额扣减
            this.rabbitmqService.sendMqMsg(TingShuMqConstants.ORDER_EXCHANGE, TingShuMqConstants.ACCOUNT_DEDUCT_RK, orderInfoVo.getTradeNo());
        } catch (Exception e) {
            //出现异常则发消息解锁余额
            this.rabbitmqService.sendMqMsg(TingShuMqConstants.ORDER_EXCHANGE, TingShuMqConstants.ACCOUNT_UNLOCK_RK, orderInfoVo.getTradeNo());
            e.printStackTrace();
        }
    }

    @Override
    public OrderInfo getOrderInfo(String orderNo) {
        OrderInfo orderInfo = this.getOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));
        if (orderInfo == null) return null;
        this.setOrderInfoParam(orderInfo);
        return orderInfo;
    }

    @Override
    public boolean updateOrderStatus(String orderNo, String orderStatusUnpaid, String orderStatusPaid) {
        return this.update(new LambdaUpdateWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo)
                .eq(OrderInfo::getOrderStatus, SystemConstant.ORDER_STATUS_UNPAID)
                .set(OrderInfo::getOrderStatus, SystemConstant.ORDER_STATUS_PAID));
    }

    @Override
    public Page<OrderInfo> findUserPage(int page, int limit) {
        Page<OrderInfo> iPage = new Page<>(page, limit);
        Long userId = AuthContextHolder.getUserId();
        Page<OrderInfo> orderInfoPage = this.page(iPage, new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getUserId, userId).orderByDesc(OrderInfo::getId));
        List<OrderInfo> orderInfos = orderInfoPage.getRecords();
        if (CollectionUtils.isEmpty(orderInfos)) return orderInfoPage;
        orderInfos.forEach(this::setOrderInfoParam);
        return orderInfoPage;

    }

    private void setOrderInfoParam(OrderInfo orderInfo) {
        orderInfo.setOrderDetailList(this.orderDetailMapper.selectList(new LambdaQueryWrapper<OrderDetail>().eq(OrderDetail::getOrderId, orderInfo.getId())));
        orderInfo.setOrderDerateList(this.orderDerateMapper.selectList(new LambdaQueryWrapper<OrderDerate>().eq(OrderDerate::getOrderId, orderInfo.getId())));
        orderInfo.setPayWayName(this.getPayWayName(orderInfo.getPayWay()));
        orderInfo.setOrderStatusName(getOrderStatusName(orderInfo.getOrderStatus()));
    }

    private String getOrderStatusName(String orderStatus) {
        return switch (orderStatus) {
            case SystemConstant.ORDER_STATUS_UNPAID -> "未支付";
            case SystemConstant.ORDER_STATUS_PAID -> "已支付";
            case SystemConstant.ORDER_STATUS_CANCEL -> "已取消";
            default -> "未知";
        };
    }

    private String getPayWayName(String payWay) {
        return switch (payWay) {
            case SystemConstant.ORDER_PAY_WAY_WEI_XIN -> "微信支付";
            case SystemConstant.ORDER_PAY_WAY_ALIPAY -> "支付宝支付";
            case SystemConstant.ORDER_PAY_ACCOUNT -> "余额支付";
            default -> "未知";
        };
    }


    private void saveOrder(OrderInfoVo orderInfoVo, Long userId) {
        List<OrderDetailVo> orderDetailVoList = orderInfoVo.getOrderDetailVoList();
        if (CollectionUtils.isEmpty(orderDetailVoList)) return;
        //保存订单信息
        OrderInfo orderInfo = new OrderInfo();
        BeanUtils.copyProperties(orderInfoVo, orderInfo);
        orderInfo.setUserId(userId);

        orderInfo.setOrderNo(orderInfoVo.getTradeNo());
        orderInfo.setOrderStatus(SystemConstant.ORDER_STATUS_UNPAID);

        switch (orderInfoVo.getItemType()) {
            case SystemConstant.ORDER_ITEM_TYPE_ALBUM -> {
                orderInfo.setOrderTitle(orderDetailVoList.get(0).getItemName());
            }
            case SystemConstant.ORDER_ITEM_TYPE_TRACK -> {
                orderInfo.setOrderTitle(orderDetailVoList.get(0).getItemName() + "及后" + orderDetailVoList.size() + "集");
            }
            case SystemConstant.ORDER_ITEM_TYPE_VIP -> {
                orderInfo.setOrderTitle(orderDetailVoList.get(0).getItemName() + "vip会员");
            }
        }
        this.save(orderInfo);

        //获取保存成功后返回的orderId
        Long orderId = orderInfo.getId();
        //保存订单明细
        orderDetailVoList.forEach(orderDetailVo -> {
            OrderDetail orderDetail = new OrderDetail();
            BeanUtils.copyProperties(orderDetailVo, orderDetail);
            orderDetail.setOrderId(orderId);
            this.orderDetailMapper.insert(orderDetail);
        });
        //保存订单满减
        List<OrderDerateVo> orderDerateVoList = orderInfoVo.getOrderDerateVoList();
        if (!CollectionUtils.isEmpty(orderDerateVoList)) {
            orderDerateVoList.forEach(orderDerateVo -> {
                OrderDerate orderDerate = new OrderDerate();
                BeanUtils.copyProperties(orderDerateVo, orderDerate);
                orderDerate.setOrderId(orderId);
                this.orderDerateMapper.insert(orderDerate);
            });
        }
    }
}
